package com.site.security;

import com.google.common.base.Strings;
import com.site.common.constant.Constant;
import com.site.controller.UserController;
import com.site.security.handler.LoginFailureHandler;
import com.site.util.JwtTokenUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;

/**
 * 验证码过滤器
 */
@Component("tokenAuthenticationFilter")
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    @Value("${security.loginUrl}")
    private String loginUrl;
    @Value("${security.openUrl}")
    private String[] openUrl;
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private LoginUserDetailsService loginUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String url = request.getRequestURI();
        try {
            // 判断当前请求 是否为登录请求
            if (url.equals(loginUrl)) {
                // 效验验证码
                this.checkCaptcha(request);
            }
            // 验证 token
            if (url.contains("/api/") && !Arrays.asList(openUrl).contains(url)) {
                this.checkToken(request);
            }
        } catch (AuthenticationException e) {
            // 交给自定义 AuthentFailureHandler 错误处理器
            loginFailureHandler.onAuthenticationFailure(request, response, e);
            return;
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 验证 token
     * @param request
     */
    private void checkToken(HttpServletRequest request) {
        String token = request.getHeader(Constant.User.ACCESS_TOKEN);
        String username = JwtTokenUtil.getSubject(token);// 获取用户名
        // 如果token或者用户名为空的话，不能通过认证
        if (Strings.isNullOrEmpty(token) || Strings.isNullOrEmpty(username)){
            throw new SecurityException("token验证失败");
        }
        UserDetails userDetails = loginUserDetailsService.loadUserByUsername(username);
        if (Objects.isNull(userDetails)){
            throw new SecurityException("token验证失败");
        }
        // 然后吧验证通过的 token 交给 security
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        // 把 request 交给 security
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // 设置为已登录
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }

    /**
     * 效验验证码
     * @param request
     */
    private void checkCaptcha(HttpServletRequest request) {
        //判断当前请求 是否为登录请求
        String code = request.getParameter("code");
        if (StringUtils.isBlank(code)) {
            throw new SecurityException("验证码不能为空");
        }
        if (!code.equalsIgnoreCase((String) request.getSession().getAttribute(UserController.CAPTCHA_CODE))) {
            throw new SecurityException("验证码错误");
        }
    }
}
